<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        //【1】一个标准的立即执行函数表达式范例。
        // 在js中，每一个函数在被调用的时候都会创建一个执行上下文，在该函数内部定义的变量和函数只能在该函数内部被使用，而正是因为这个上下文，使得我们在调用函数的时候能创建一些私有变量。
        // makeCounter函数返回的是一个新的函数，该函数对makeCounter里的局部变量i享有使用权
        function makeCounter() {
            // i只是makeCounter函数内的局部变量
            var i = 0;

            return function() {
                console.log(++i);
            };
        }

        //注意counter和counter2是不同的实例，它们分别拥有自己范围里的i变量

        var counter = makeCounter();
        counter(); // 1
        counter(); // 2

        var counter2 = makeCounter();
        counter2(); // 1
        counter2(); // 2
        console.log(i); // 报错，i没有定义，它只是makeCounter内部的局部变量

        var foo = function() {
            /* code */
            console.log(1);
        };
        foo();

        //【2】函数声明的语法：function后面必须要有一个函数名。
        // 报错了，这是为何？这是因为在javascript代码解释时，当遇到function关键字时，会默认把它当做是一个函数声明，而不是函数表达式，
        // 如果没有把它显视地表达成函数表达式，就报错了，因为函数声明需要一个函数名，而上面的代码中函数没有函数名。（以上代码，
        // 也正是在执行到第一个左括号(时报错，因为(前理论上是应该有个函数名的。）
        // function(){ /* code */ }(); 
        // SyntaxError: Unexpected token (
        //【3】在一个语句后面加上括号，该括号完全和之前的语句不搭嘎，而只是一个分组操作符，用来控制运算中的优先级（小括号里的先运算）。所以以上代码等价于：
        // function foo(){ /* code */ }
        // (); 
        // SyntaxError: Unexpected token )
        // 相当于先声明了一个叫foo的函数，之后进行()内的表达式运算，但是()（分组操作符）内的表达式不能为空，所以报错。（以上代码，也就是执行到右括号时，发现表达式为空，所以报错）。


        // function foo(){ /* code */ }(); 
        // SyntaxError: Unexpected token )
        //【4】立即执行函数语法：将上述的语句后面跟着的小括号（），全部用小括号（）包起来
        // 为什么这样就能立即执行并且不报错呢？因为在javascript里，括号内部不能包含语句，【注意这个地方，小括号内不能包含语句】
        // 当解析器对代码进行解释的时候，先碰到了()，然后碰到function关键字就会自动将()里面的代码识别为函数表达式而不是函数声明。


        // 而立即执行函数并非只有上面的一种写法，写法真是五花八门：
        // 最常用的两种写法
        (function() { /* code */ }()); // 老道推荐写法
        (function() { /* code */ })(); // 当然这种也可以

        // 括号和JS的一些操作符（如 = && || ,等）可以在函数表达式和函数声明上消除歧义
        // 如下代码中，解析器已经知道一个是表达式了，于是也会把另一个默认为表达式
        // 但是两者交换则会报错
        var i = function() {
            return 10;
        }();
        true && function() { /* code */ }();
        0,
        function() { /* code */ }();

        // 如果你不怕代码晦涩难读，也可以选择一元运算符
        ! function() { /* code */ }();
        ~ function() { /* code */ }(); -

        function() { /* code */ }(); +

        function() { /* code */ }();

        // 你也可以这样
        new function() { /* code */ }
        new function() { /* code */ }() // 带参数
        //【5】代码习惯问题
        // 无论何时，给立即执行函数加上括号是个好习惯
        // 有的时候，我们实际上不需要使用()使之变成一个函数表达式，啥意思？比如下面这行代码，其实不加上()也不会保错：
        var i = function() {
            return 10;
        }();
        //但是我们依然推荐加上()：
        var i = (function() {
            return 10;
        }());
        //原因：比如上述代码，如果function内部代码量庞大，我们不得不滚动到最后去查看function(){}后是否带有()来确定i值是个function还是
        //一个IIFE的返回值。所以为了代码的可读性，请尽量加上()无论是否已经是表达式。
    </script>
</body>

</html>